#include "qlsdbconnpool.h"

#include <QSettings>
#include <QTextCodec>
#include <QMutexLocker>

QMutex QLsDBConnPool::m_instanceMutex;
QWaitCondition QLsDBConnPool::m_waitConnection;
QLsDBConnPool* QLsDBConnPool::m_instance = NULL;

QLsDBConnPool::QLsDBConnPool()
{
	/* 从ini配置文件中读取配置信息 */
	QSettings settings("ini/connectionPool.ini", QSettings::IniFormat);
	settings.setIniCodec(QTextCodec::codecForName("utf-8"));

	/* 获取数据库通用配置 */
	settings.beginGroup("database");

	m_type = settings.value("type", "MySQL").toString();						/* 数据库类型 */
	m_testOnBorrow = settings.value("testOnBorrow", 1).toUInt();				/* 从连接池获取一个连接时，取出是否检测 */
	m_maxWaitTime = settings.value("maxWaitTime", 1000).toInt();				/* 获取连接最大等待时间 */
	m_waitInterval = settings.value("waitInterval", 200).toInt();				/* 尝试获取连接时等待间隔时间 */
	m_maxConnectionCount = settings.value("maxConnectionCount", 5).toInt();		/* 最大连接数 */
	m_isSQL = settings.value("isSQL", 1).toUInt();								/* 是否输出sql语句 */

	settings.endGroup();

	/* 获取数据库信息 */
	settings.beginGroup(m_type);

	/* mysql */
	if(m_type == "MySQL")
	{
		m_databaseType = settings.value("databaseType", "QMYSQL").toString();
		m_hostName = settings.value("hostName", "localhost").toString();
		m_port = settings.value("port", "3306").toUInt();
		m_databaseName = settings.value("databaseName", "qlpmsp").toString();
		m_username = settings.value("username", "root").toString();
		m_password = settings.value("password", "admin").toString();
	}
	/* sqlite */
	else if(m_type == "SQLite")
	{
		m_databaseType = settings.value("databaseType", "QSQLITE").toString();
		m_databaseName = settings.value("databaseName", "databases/sqlite/qlpmsp.db").toString();
		/* 路径校验 - 数据库的数据文件必须存在路径，才能创建数据库文件 */
		do
		{
			/* 获取最后一个文件分隔符的位置 */
			int lastIndex = -1;
			if((lastIndex = m_databaseName.lastIndexOf("/")) == -1 &&
					(lastIndex = m_databaseName.lastIndexOf("\\")) == -1)
				break;
			/* 提取数据文件的路径 */
			QString path(m_databaseName.left(lastIndex));
			/* 校验路径是否存在，如果不存在则创建 */
			QDir dir = QDir(qApp->applicationDirPath());
			if(!dir.cd(path))
				dir.mkpath(path);
		} while(0);
	}

	settings.endGroup();

	m_testOnBorrowSQL = "select 1;";
}

/* 析构函数 */
QLsDBConnPool::~QLsDBConnPool()
{
	/* 销毁连接池的时候删除所有的连接 */
	foreach(QString connectionName, m_usedConnectionNames)
		QSqlDatabase::removeDatabase(connectionName);

	foreach(QString connectionName, m_unusedConnectionNames)
		QSqlDatabase::removeDatabase(connectionName);
}

/* 获取数据库连接 */
QSqlDatabase QLsDBConnPool::getConnection()
{
	QLsDBConnPool& pool = QLsDBConnPool::getInstance();
	QString connectionName;

	QMutexLocker locker(&m_instanceMutex);

	/* 已创建连接数 */
	int connectionCount = pool.m_unusedConnectionNames.size() + pool.m_usedConnectionNames.size();

	/* 如果连接已经用完，等待 waitInterval 毫秒看看是否有可用连接，最长等待 maxWaitTime 毫秒 */
	for (int i = 0; i < pool.m_maxWaitTime && pool.m_unusedConnectionNames.size() == 0 &&
		 connectionCount == pool.m_maxConnectionCount; i += pool.m_waitInterval)
	{
		m_waitConnection.wait(&m_instanceMutex, pool.m_waitInterval);

		/* 重新计算已创建连接数 */
		connectionCount = pool.m_unusedConnectionNames.size() + pool.m_usedConnectionNames.size();
	}

	if (pool.m_unusedConnectionNames.size() > 0)
	{	/* 有已经回收的连接，复用它们 */
		connectionName = pool.m_unusedConnectionNames.front();
		pool.m_unusedConnectionNames.pop_front();
	}
	else if (connectionCount < pool.m_maxConnectionCount)
	{	/* 没有已经回收的连接，但是没有达到最大连接数，则创建新的连接 */
		connectionName = QString("Connection-%1").arg(connectionCount + 1);
	}
	else
	{	/* 已经达到最大连接数 */
		qDebug() << "Cannot create more connections.";
		return QSqlDatabase();
	}

	/* 创建连接 */
	QSqlDatabase db = pool.createConnection(connectionName);

	/* 有效的连接才放入 usedConnectionNames */
	if (db.isOpen())
		pool.m_usedConnectionNames.push_back(connectionName);

	return db;
}

/* 释放数据库连接回连接池 */
void QLsDBConnPool::releaseConnection(QSqlDatabase connection)
{
	QLsDBConnPool& pool = QLsDBConnPool::getInstance();
	QString connectionName = connection.connectionName();

	/* 如果是我们创建的连接，从 used 里删除，放入 unused 里 */
	if (pool.m_usedConnectionNames.contains(connectionName))
	{
		QMutexLocker locker(&m_instanceMutex);
		pool.m_usedConnectionNames.removeOne(connectionName);
		pool.m_unusedConnectionNames.push_back(connectionName);
		m_waitConnection.wakeOne();
	}
}

/* 关闭所有的数据库连接 */
void QLsDBConnPool::destoryConnectionPool()
{
	QMutexLocker locker(&m_instanceMutex);
	delete m_instance;
	m_instance = NULL;
}

/* select语句 */
QSqlRecord *QLsDBConnPool::runOne(const QString &content)
{
	QLsDBConnPool &pool = QLsDBConnPool::getInstance();

	/* 输出sql语句 */
	if(pool.m_isSQL)
		qDebug() << "sql:" << content;

	/* 打开数据库 */
	QSqlDatabase db = QLsDBConnPool::getConnection();

	/* 执行sql语句 */
	QSqlQuery query(db);
	query.exec(content);
	if(query.isValid())
	{
		qDebug() << "Invalid record: " << query.lastError();
		QLsDBConnPool::releaseConnection(db);
		exit(EOF);
	}

	/* 获取查询结果 */
	QSqlRecord* rec = nullptr;
	if(!query.first())
	{
		qDebug() << "no result!";
		QLsDBConnPool::releaseConnection(db);
		return nullptr;
	}
	else
	{
		rec = new QSqlRecord(query.record());
		QLsDBConnPool::releaseConnection(db);
		return rec;
	}
}

/* insert into语句和update语句 */
void QLsDBConnPool::runNo(const QString &content)
{
	QLsDBConnPool &pool = QLsDBConnPool::getInstance();

	/* 输出sql语句 */
	if(pool.m_isSQL)
		qDebug() << "sql:" << content;

	/* 打开数据库 */
	QSqlDatabase db = QLsDBConnPool::getConnection();

	/* 执行sql语句 */
	QSqlQuery query(db);
	query.exec(content);
	if(query.isValid())
	{
		qDebug() << "Invalid record: " << query.lastError();
		QLsDBConnPool::releaseConnection(db);
		exit(EOF);
	}

	QLsDBConnPool::releaseConnection(db);
}

//select
QList<QSqlRecord> QLsDBConnPool::runAll(const QString &content)
{
	QLsDBConnPool &pool = QLsDBConnPool::getInstance();

	/* 输出sql语句 */
	if(pool.m_isSQL)
		qDebug() << "sql:" << content;

	/* 打开数据库 */
	QSqlDatabase db = QLsDBConnPool::getConnection();

	/* 执行sql语句 */
	QSqlQuery query(db);
	query.exec(content);
	if(query.isValid())
	{
		qDebug() << "Invalid record: " << query.lastError();
		QLsDBConnPool::releaseConnection(db);
		exit(EOF);
	}

	/* 获取查询结果 */
	QList<QSqlRecord> res;
	while(query.next())
	{
		QSqlRecord rec(query.record());
		res.push_back(rec);
	}

	QLsDBConnPool::releaseConnection(db);
	return res;
}

/* 单例模式：获取数据库连接池对象 */
QLsDBConnPool &QLsDBConnPool::getInstance()
{
	/*
	 * 这里判断NULL用了互斥锁，还可以直接：
	 * static ConnectionPooL g_instance;
	 * return g_instance;
	 * 具体是否有什么区别，暂时不知道
	 */
	if (NULL == m_instance)
	{
		QMutexLocker locker(&m_instanceMutex);

		if (NULL == m_instance)
			m_instance = new QLsDBConnPool();
	}

	return *m_instance;
}

/* 创建数据库连接 */
QSqlDatabase QLsDBConnPool::createConnection(const QString &connectionName)
{
	/* 连接已经创建过了，复用它，而不是重新创建 */
	if (QSqlDatabase::contains(connectionName))
	{
		/* 连接前访问的数据库 */
		QSqlDatabase db1 = QSqlDatabase::database(connectionName);

		/* 取出检测 */
		if (m_testOnBorrow)
		{
			if(m_isSQL) qDebug() << "Test connection on borrow, execute:" << m_testOnBorrowSQL << ", for" << connectionName;
			QSqlQuery query(m_testOnBorrowSQL, db1);

			/* 如果连接断开，重新建立连接 */
			if (query.lastError().type() != QSqlError::NoError && !db1.open())
			{
				qDebug() << "Open datatabase error:" << db1.lastError().text();
				return QSqlDatabase();
			}
		}

		return db1;
	}

	/* 创建一个新的连接 */
	QSqlDatabase db = QSqlDatabase::addDatabase(m_databaseType, connectionName);
	if(m_type == "MySQL")
	{
		db.setHostName(m_hostName);
		db.setPort(m_port);
		db.setDatabaseName(m_databaseName);
		db.setUserName(m_username);
		db.setPassword(m_password);
	}
	else if(m_type == "SQLite")
	{
		db.setDatabaseName(m_databaseName);
	}
	else
	{
		qDebug() << "datatabase type error:" << m_type;
		return QSqlDatabase();
	}

	if (!db.open())
	{
		qDebug() << "Open datatabase error:" << db.lastError().text();
		return QSqlDatabase();
	}

	return db;
}

QString QLsDBConnPool::getType()
{
	QLsDBConnPool &pool = QLsDBConnPool::getInstance();
	return pool.m_type;
}
